/*
 * Copyright 2023 OpenSPG Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied.
 */

package com.antgroup.openspg.core.schema.model.predicate;

import com.antgroup.openspg.core.schema.model.BasicInfo;
import com.antgroup.openspg.core.schema.model.identifier.PredicateIdentifier;
import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum;
import com.antgroup.openspg.core.schema.model.semantic.SystemPredicateEnum;
import com.antgroup.openspg.core.schema.model.type.EntityType;
import com.antgroup.openspg.core.schema.model.type.SPGTypeRef;

/**
 * Class definition of relation<br>
 *
 * <p>A relation is usually defined between binary objects to describe the existence of a certain
 * connection between two entities. Relation can be regarded as a special property and is also SPO
 * triplet structure, with object types generally being {@link EntityType}.
 *
 * <p>Like {@code Property}, we can define some sub properties for {@code Relation} to describe its
 * extended information, and we can use {@code SystemPredicate} to define the semantics between
 * {@code Relation}. Also, we can define KGDSL script to calculate whether a {@code Relation}
 * exists.
 *
 * <p>Usually the instance of relation is called RelationRecord, and the schema of relation is
 * called Relation.
 */
public class Relation extends Property {

  private static final long serialVersionUID = -6159117175859916947L;

  /** If the relation is generated by the semantic property */
  private final Boolean isDynamic;

  public Relation(
      BasicInfo<PredicateIdentifier> basicInfo,
      SPGTypeRef subjectTypeRef,
      SPGTypeRef objectTypeRef,
      Boolean inherited,
      PropertyAdvancedConfig advancedConfig) {
    this(basicInfo, subjectTypeRef, objectTypeRef, inherited, advancedConfig, false);
  }

  public Relation(
      BasicInfo<PredicateIdentifier> basicInfo,
      SPGTypeRef subjectTypeRef,
      SPGTypeRef objectTypeRef,
      Boolean inherited,
      PropertyAdvancedConfig advancedConfig,
      Boolean isSemanticRelation) {
    super(basicInfo, subjectTypeRef, objectTypeRef, inherited, advancedConfig);
    this.isDynamic = isSemanticRelation;
  }

  /**
   * If the relation is transitive.
   *
   * @return true or false
   */
  public boolean isTransitive() {
    return withPredicate(SystemPredicateEnum.TRANSITIVE);
  }

  /**
   * If the relation is symmetric.
   *
   * @return true or false
   */
  public boolean isSymmetric() {
    return withPredicate(SystemPredicateEnum.SYMMETRIC);
  }

  public boolean withPredicate(SystemPredicateEnum systemPredicate) {
    return systemPredicate != null
        && getAdvancedConfig() != null
        && getAdvancedConfig().getSemantics() != null
        && getAdvancedConfig().getSemantics().stream()
            .anyMatch(e -> systemPredicate.getName().equals(e.getPredicateIdentifier().getName()));
  }

  /**
   * If the relation is generated by the property.
   *
   * @return true or false
   */
  public boolean isSemanticRelation() {
    return isDynamic;
  }

  @Override
  public PropertyRef toRef() {
    return new PropertyRef(
        this.getSubjectTypeRef(),
        this.getBasicInfo(),
        this.getObjectTypeRef(),
        SPGOntologyEnum.RELATION,
        this.getProjectId(),
        this.getOntologyId());
  }
}
